package rest

import (
	"gitee.com/gaodihu/gsf/core/discov"
	"gitee.com/gaodihu/gsf/core/logx"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
)

type (
	runOptions struct {
		start func(*engine)
	}
	RunOption func(*Server)

	Server struct {
		engine *engine
		opts   runOptions
		publish  *discov.Publisher
	}
)

func MustNewServer(c RestConf, opts ...RunOption) *Server {
	server, err := NewServer(c, opts...)
	if err != nil {
		log.Fatalln(err)
	}
	return server
}

func NewServer(c RestConf, opts ...RunOption) (*Server, error) {
	server := &Server{
		engine: NewEngine(c),
		opts: runOptions{
			start: func(e *engine)  {
				 e.Start()
			},
		},
	}
	for _, opt := range opts {
		opt(server)
	}
	return server, nil
}

func (s *Server) AddRoutes(UrlPrefix string, rs []Route, opts ...RouteOption) {
	r := featuredRoutes{
		urlPrefix: UrlPrefix,
		routes:    rs,
	}
	for _, opt := range opts {
		opt(&r)
	}
	s.engine.AddRoutes(r)
}

func (s *Server) AddRoute(urlPrefix string, r Route, opts ...RouteOption) {
	s.AddRoutes(urlPrefix, []Route{r}, opts...)
}

func (s *Server) Start() {
	if err := s.registry(); err != nil {
		logx.ErrorWithStack(err)
	}
	defer s.close()
	s.opts.start(s.engine)
}
func (s *Server) close() {
	s.publish.Revoke()
}

func (s *Server) Use(m ...gin.HandlerFunc) {
	s.engine.middlewares = append(s.engine.middlewares, m...)
}
func (s *Server) registry() error {
	s.AddRoute("", Route{
		Method: http.MethodGet,
		Path:   "/health",
		Handler: func(ctx *gin.Context) {
			ctx.Writer.Header().Add("Content-Type", "application/json")
			ctx.Writer.WriteString(`{"status":"UP"}`)
		},
	})
	isSecure := s.engine.isSecure()
	publish := discov.NewPublisher(s.engine.conf.Consul.Host, s.engine.conf.Consul.Key, s.engine.conf.Host, s.engine.conf.Port, isSecure)
	s.publish = publish
	return publish.Register()
}

func handleError(err error) {
	// ErrServerClosed means the server is closed manually
	if err == nil || err == http.ErrServerClosed {
		return
	}

	logx.ErrorWithStack(err)
}

func UseRoutesMiddleWares(m ...gin.HandlerFunc) func(f *featuredRoutes) {
	return func(f *featuredRoutes) {
		f.middlerwares = append(f.middlerwares, m...)
	}
}
